<?php
declare(strict_types=1);

namespace SixShop\Lakala;

use SixShop\Lakala\Dto\LocationInfo;
use SixShop\Lakala\Facade\TransactionService;
use SixShop\Lakala\OpenAPISDK\V3\Model\TradePreorderWechaAccBusiFields;
use SixShop\Lakala\OpenAPISDK\V3\Model\TradePreorderWechaDetail;
use SixShop\Lakala\OpenAPISDK\V3\Model\TradePreorderWechaGoodsDetail;
use SixShop\Payment\Contracts\PaymentNotifyResult;
use SixShop\Payment\Contracts\PaymentProviderInterface;
use SixShop\Payment\Contracts\PaymentQueryResult;
use SixShop\Payment\Contracts\PaymentRefundRequest;
use SixShop\Payment\Contracts\PaymentRefundResult;
use SixShop\Payment\Contracts\PaymentResponse;
use SixShop\Payment\Entity\ExtensionPaymentEntity;
use SixShop\Payment\Enum\PaymentBizEnum;
use SixShop\Payment\Enum\PaymentStatusEnum;
use SixShop\Payment\Event\PaymentSuccessEvent;
use think\facade\Event;

class PaymentProvider implements PaymentProviderInterface
{
    public function __construct(
        private readonly Config $config,
        private readonly ExtensionPaymentEntity $extensionPaymentEntity,
    )
    {
    }

    #[\Override] public function create(array $order, PaymentBizEnum $bizType): PaymentResponse
    {
        $payment = $this->extensionPaymentEntity->where([
            'order_id' => $order['id'],
            'pay_type' => Extension::EXTENSION_ID,
            'biz_type' => $bizType,
        ])->findOrEmpty();
        if (!$payment->isEmpty()) {
            // todo 判断订单是否支付成功
            // 支付时间结束关闭订单
            // 订单未支付可重新支付
            // 交易已关闭请重新下单
            //订单已付款请勿重复操作
            // 订单已退款请重新下单
            //订单已撤销请重新下单
            // 订单支付中请稍后再试
            // 订单支付失败请重新下单
            throw new  \RuntimeException('开发测试中，请稍后再试');
        }
        $payment->transaction(function () use ($bizType, $order, $payment) {
            $randomDiscount = mt_rand(0, intval($this->config->random_discount_max * 100)) / 100;
            $order['pay_amount'] = $order['pay_amount'] - $randomDiscount;
            $order['pay_amount'] = max(0.01, round($order['pay_amount'], 2));
            $payment->save([
                'user_id' => $order['user_id'],
                'order_id' => $order['id'],
                'order_sn' => $order['order_sn'],
                'biz_type' => $bizType,
                'pay_type' => Extension::EXTENSION_ID,
                'amount' => $order['pay_amount'],
                'status' => PaymentStatusEnum::PENDING
            ]);
            $expireDuration = 15; // 分钟
            $payment->expire_time = time() + $expireDuration * 60;
            $payment->payment_param = $this->createPaymentParam($order, $payment, $expireDuration);
            $payment->transaction_id = $payment->payment_param->trade_no;
            $payment->save();
        });
        return new PaymentResponse(orderNo: $payment->out_trade_no, type: Extension::EXTENSION_ID, raw: $payment->toArray());
    }

    #[\Override] public function notify(array $request): PaymentNotifyResult
    {
        throw new \Exception('Not implemented');
    }

    #[\Override] public function query(int $recordID): PaymentQueryResult
    {
        $payment = $this->extensionPaymentEntity->read($recordID);
        if ($payment->status === PaymentStatusEnum::PENDING) {
            $response = TransactionService::queryTrade($payment->transaction_id);
            // INIT-初始化 CREATE-下单成功 SUCCESS-交易成功 FAIL-交易失败 DEAL-交易处理中 UNKNOWN-未知状态 CLOSE-订单关闭 PART_REFUND-部分退款 REFUND-全部退款(或订单被撤销）
            $payment->status = match ($response->trade_state) {
                'DEAL' => PaymentStatusEnum::PENDING,
                'SUCCESS' => PaymentStatusEnum::SUCCESS,
                'FAIL' => PaymentStatusEnum::FAIL,
                'CLOSE' => PaymentStatusEnum::CLOSED,
                default => PaymentStatusEnum::PENDING,
            };
            if ($payment->status !== PaymentStatusEnum::PENDING) {
                $payment->status_desc = $response->trade_state_desc;
                $payment->payment_result = $response;
                if ($payment->status === PaymentStatusEnum::SUCCESS) {
                    $payment->payment_time = time();
                    $payment->save();
                    Event::trigger(new PaymentSuccessEvent($payment['order_sn'], Extension::EXTENSION_ID, $payment->toArray(), $payment->biz_type));
                } else {
                    $payment->save();
                }
            }
        }
        return new PaymentQueryResult(
            orderNo: $payment['out_trade_no'],
            status: $payment['status'],
            amount: (float)$payment['amount'],
            raw: $payment->toArray()
        );
    }

    #[\Override] public function refund(int $recordID, PaymentRefundRequest $param): PaymentRefundResult
    {
        throw new \Exception('Not implemented');
    }

    #[\Override] public function refundQuery(int $refundID): PaymentRefundResult
    {
        throw new \Exception('Not implemented');
    }

    private function createPaymentParam(array $order, ExtensionPaymentEntity $payment, int $expireDuration): object
    {
        $accBusiFields = new TradePreorderWechaAccBusiFields();
        $accBusiFields->setTimeoutExpress($expireDuration);
        $accBusiFields->setSubAppid($this->config->sub_appid);
        $accBusiFields->setUserId($order['params']['openid']);
        /*$detail = new TradePreorderWechaDetail();
        $detail->setCostPrice((float)$order['pay_amount']);
        $detail->setReceiptId('');
        $goodsDetailList = [];
        foreach ($order['order_goods'] as $goods) {
            $goodsDetail = new TradePreorderWechaGoodsDetail();
            $goodsDetail->setGoodsId((string)$goods['goods_id']);
            $goodsDetail->setQuantity($goods['num']);
            $goodsDetail->setWxpayGoodsId('');
            $goodsDetail->setPrice(round($goods['price']*100, 0));
            $goodsDetail->setGoodsName($goods['goods_name']);
            $goodsDetailList[] = $goodsDetail;
        }
        $detail->setGoodsDetail($goodsDetailList);
        $accBusiFields->setDetail($detail);*/
        return TransactionService::preOrder(
            outTradeNo: $payment['out_trade_no'],
            totalAmount: $payment['amount'],
            locationInfo: new LocationInfo(requestIP: $order['params']['ip']),
            subject: '订单：' . $order['order_sn'],
            settleType: '1', // 拉卡拉分账
            remark: $order['description'],
            accBusiFields: $accBusiFields,
        );
    }
}